#include "myAllocHierarchy.h"

HRESULT myAllocHierarchy::CreateFrame(LPCSTR Name, LPD3DXFRAME * ppNewFrame) 
{
	// setup result variable
	HRESULT res = S_OK;
	*ppNewFrame = NULL;
	// create frame in memory
	D3DXFRAME *fr = NULL;
	fr = new D3DXFRAME();
	if (!fr) {
		res = E_OUTOFMEMORY;
		goto cf_exit;
	}
	// copy frame's name
	res = AllocateName(Name, &fr->Name);
	if (FAILED(res))
		goto cf_exit;
	// create identity matrix for this frame
	D3DXMatrixIdentity(&fr->TransformationMatrix);
	// clear followin fields: child, sibling, meshcontainer
	fr->pFrameFirstChild = NULL;
	fr->pFrameSibling = NULL;
	fr->pMeshContainer = NULL;
	// copy the frame's pointer to OUT variable
	*ppNewFrame = fr;
	// clear reference to the frame so it cannot be 'delete'd
	fr = NULL;
cf_exit:
	delete fr;
	return res;
}

HRESULT myAllocHierarchy::CreateMeshContainer(LPCSTR Name, CONST D3DXMESHDATA * pMeshData,
								   CONST D3DXMATERIAL * pMaterials, CONST D3DXEFFECTINSTANCE * pEffectInstances,
								   DWORD NumMaterials, CONST DWORD * pAdjacency, LPD3DXSKININFO pSkinInfo,
								   LPD3DXMESHCONTAINER * ppNewMeshContainer) 
{
	// setup initial variables
	HRESULT hr = S_OK;
	my_D3DXMESHCONTAINER *mc = NULL;
	UINT numFaces, iMat, iBone, cBones;
	LPDIRECT3DDEVICE9 pDev = NULL;
	LPD3DXMESH pMesh = NULL;
	*ppNewMeshContainer = NULL;
	// if mesh type == D3DXMESHTYPE_MESH
	if (D3DXMESHTYPE_MESH != pMeshData->Type) {
		hr = E_FAIL;
		goto cmc_exit;
	}
	// assign mesh pointer to new variable
	pMesh = pMeshData->pMesh;
	// support meshes with FVF only (4now)!!
	if (0 == pMesh->GetFVF()) {
		hr = E_FAIL;
		goto cmc_exit;
	}
	// allocate memory for new MeshContainer
	mc = new my_D3DXMESHCONTAINER();
	if (!mc) {
		hr = E_OUTOFMEMORY;
		goto cmc_exit;
	}
	// zero out the structure
	memset(mc, 0, sizeof(my_D3DXMESHCONTAINER));
	// copy structure's name
	hr = AllocateName(Name, &mc->Name);
	if (FAILED(hr)) 
		goto cmc_exit;
	pMesh->GetDevice(&pDev);
	numFaces = pMesh->GetNumFaces();
	// lets assume there are normals included in .x file
	if (pMesh->GetFVF() & D3DFVF_NORMAL) {
		mc->MeshData.pMesh = pMesh;
		mc->MeshData.Type = D3DXMESHTYPE_MESH;
		pMesh->AddRef();
	} else {
		hr = E_FAIL;
		goto cmc_exit;
	}
	// allocate memory for materials and adjacency info
	mc->NumMaterials = max(1, NumMaterials);
	mc->pMaterials = new D3DXMATERIAL [mc->NumMaterials];
	mc->pAdjacency = new DWORD [numFaces * 3];
	if ((!mc->pAdjacency) || (!mc->pMaterials)) {
		hr = E_OUTOFMEMORY;
		goto cmc_exit;
	}
	// copy adjacency info
	memcpy(mc->pAdjacency, pAdjacency, sizeof(DWORD) * numFaces * 3);
	// if there are >0 materials we should load textures (TO DO)
	if (0 < NumMaterials) {
		memcpy(mc->pMaterials, pMaterials, sizeof(D3DXMATERIAL) * NumMaterials);
		for (iMat = 0; iMat < NumMaterials; iMat++){
			mc->pMaterials[iMat].pTextureFilename = NULL;
		}
	// if there is no material in the file we load it with default gray color
	} else {
		mc->pMaterials[0].pTextureFilename = NULL;
		memset(&mc->pMaterials[0].MatD3D, 0, sizeof(D3DMATERIAL9));
		mc->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
		mc->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
		mc->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
		mc->pMaterials[0].MatD3D.Diffuse.a = 1.0f;
		mc->pMaterials[0].MatD3D.Specular = mc->pMaterials[0].MatD3D.Diffuse;
	}
	// here we load Skin information
	if (pSkinInfo) {
		mc->pSkinInfo = pSkinInfo;
		pSkinInfo->AddRef();
		mc->pOrigMesh = pMesh;
		pMesh->AddRef();
		cBones = pSkinInfo->GetNumBones();
		mc->pBoneOffsetMatrices = new D3DXMATRIX [cBones];
		if (!mc->pBoneOffsetMatrices) {
			hr = E_OUTOFMEMORY;
			goto cmc_exit;
		}
		for (iBone = 0; iBone < cBones; iBone++){
			D3DXMatrixIdentity(&mc->pBoneOffsetMatrices[iBone]);// = *(mc->pSkinInfo->GetBoneOffsetMatrix(iBone));
		/*	mc->pBoneOffsetMatrices[iBone] = *(mc->pSkinInfo->GetBoneOffsetMatrix(iBone));
			mc->pBoneOffsetMatrices[iBone]._41 = 0.0f;
			mc->pBoneOffsetMatrices[iBone]._42 = 0.0f;
			mc->pBoneOffsetMatrices[iBone]._43 = 0.0f;*/
		}
		hr = GenerateSkinnedMesh(pDev, mc);
		if (FAILED(hr))
			goto cmc_exit;
	}
	// copy pointer to OUT variable
	*ppNewMeshContainer = (D3DXMESHCONTAINER*) mc;
	// set pointer to null so that it won't be 'delete'd
	mc = NULL;
cmc_exit:
	SAFE_RELEASE(pDev);
	if (mc)
		DestroyMeshContainer(mc);
	return hr;
}

HRESULT myAllocHierarchy::DestroyFrame(LPD3DXFRAME pFrameToFree)
{
	SAFE_DELETE_ARRAY(pFrameToFree->Name);
	SAFE_DELETE(pFrameToFree);
	return S_OK;
}

HRESULT myAllocHierarchy::DestroyMeshContainer(LPD3DXMESHCONTAINER pMeshContainerToFree)
{
	my_D3DXMESHCONTAINER *mc = (my_D3DXMESHCONTAINER*) pMeshContainerToFree;
	SAFE_DELETE_ARRAY(mc->Name);
	SAFE_DELETE_ARRAY(mc->pAdjacency);
	SAFE_DELETE_ARRAY(mc->pMaterials);
	SAFE_DELETE_ARRAY(mc->pBoneOffsetMatrices);
	SAFE_RELEASE(mc->MeshData.pMesh);
	SAFE_RELEASE(mc->pSkinInfo);
	SAFE_RELEASE(mc->pOrigMesh);
	SAFE_DELETE(mc);
	return S_OK;
}




// helper functions
HRESULT myAllocHierarchy::AllocateName( LPCSTR Name, LPSTR *pNewName )
{
    UINT cbLength;

    if( Name != NULL )
    {
        cbLength = (UINT)strlen(Name) + 1;
        *pNewName = new CHAR[cbLength];
        if (*pNewName == NULL)
            return E_OUTOFMEMORY;
        memcpy( *pNewName, Name, cbLength*sizeof(CHAR) );
    }
    else
    {
        *pNewName = NULL;
    }

    return S_OK;
}

HRESULT myAllocHierarchy::GenerateSkinnedMesh( IDirect3DDevice9 *pd3dDevice, 
											   my_D3DXMESHCONTAINER *pMC )
{
	HRESULT hr = S_OK;
	if (!pMC->pSkinInfo) 
		return hr;
	SAFE_RELEASE(pMC->MeshData.pMesh);
	hr = pMC->pOrigMesh->CloneMeshFVF(D3DXMESH_MANAGED, pMC->pOrigMesh->GetFVF(), pd3dDevice, &pMC->MeshData.pMesh);
	if (FAILED(hr))
		goto gsm_exit;
gsm_exit:
	return hr;
}